# _*_ coding: utf-8 _*_
"""
Time:     2024/8/21
Author:   2051261
File:     create_engine.py
"""
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
from typing import Dict, Type
from dataclasses import dataclass
from urllib.parse import quote_plus
import logging

from mine.project.log import log_manager as logger

logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING)


@dataclass
class ConnectionPoolConfig:
    conn_name: str
    database_type: str
    database_name: str
    database_ip: str
    database_port: int
    user: str
    password: str
    max_open_connections: int = 50
    max_idle_connections: int = 20
    connection_max_lifetime: int = 1  # Lifetime in minutes

    def validate(self) -> None:
        """验证配置的正确性"""
        if not self.database_type:
            raise ValueError("Database type is required.")
        if not all([self.database_name, self.database_ip, self.database_port, self.user, self.password]):
            raise ValueError("Database configuration parameters cannot be empty.")


class AsyncDatabaseEngineFactory:

    def __init__(self, config: ConnectionPoolConfig):
        self.config = config
        self.config.validate()  # 验证配置
        self._quote_config()

    async def create_engine(self) -> AsyncEngine:
        connection_string = await self._build_connection_string()
        safe_connection_string = await self._mask_sensitive_info(connection_string)
        logger.info(f"Creating Engine with connection string: {safe_connection_string}")
        return create_async_engine(
            connection_string,
            pool_size=self.config.max_idle_connections,
            pool_pre_ping=True,
            max_overflow=self.config.max_open_connections - self.config.max_idle_connections,
            pool_recycle=28800,  # self.config.connection_max_lifetime * 60,  # Convert minutes to seconds
            echo=False
        )

    def _quote_config(self) -> None:
        self.config.user = quote_plus(self.config.user)
        self.config.password = quote_plus(self.config.password)
        self.config.database_name = quote_plus(self.config.database_name)
        self.config.database_ip = quote_plus(self.config.database_ip)

    async def _build_connection_string(self) -> str:
        raise NotImplementedError("Subclasses must implement this method")

    @staticmethod
    async def _mask_sensitive_info(connection_string: str) -> str:
        # 使用正则表达式替换密码部分
        import re
        masked_string = re.sub(r':[^@]+@', ':******@', connection_string)
        return masked_string


class AsyncMySQLDatabaseEngineFactory(AsyncDatabaseEngineFactory):
    async def _build_connection_string(self) -> str:
        return (
            f"mysql+aiomysql://{self.config.user}:{self.config.password}@"
            f"{self.config.database_ip}:{self.config.database_port}/{self.config.database_name}?charset=utf8mb4"
        )


# class MsSQLDatabaseEngineFactor(DatabaseEngineFactory):
#     def _build_connection_string(self) -> str:
#         driver = quote_plus('ODBC Driver 17 for SQL Server')
#         return (
#             f"mssql+pyodbc://{self.config.user}:{self.config.password}@{self.config.database_ip}:"
#             f"{self.config.database_port}/{self.config.database_name}?driver={driver}"
#         )


class AsyncMSSQLDatabaseEngineFactory(AsyncDatabaseEngineFactory):
    async def _build_connection_string(self) -> str:
        return (
            f"mssql+aioodbc://{self.config.user}:{self.config.password}@"
            f"{self.config.database_ip}:{self.config.database_port}/{self.config.database_name}"
        )


class AsyncDatabaseEngineFactoryProvider:
    factories: Dict[str, Type[AsyncDatabaseEngineFactory]] = {
        'mysql': AsyncMySQLDatabaseEngineFactory,
        'mssql': AsyncMSSQLDatabaseEngineFactory,
    }

    @classmethod
    async def get_factory(cls, config: ConnectionPoolConfig) -> AsyncDatabaseEngineFactory:
        factory_class = cls.factories.get(config.database_type.lower())
        if not factory_class:
            raise ValueError(f"Unsupported database type: {config.database_type}")
        return factory_class(config)


async def get_async_engine_from_config(config: ConnectionPoolConfig) -> AsyncEngine:
    """根据配置获取数据库 engine """
    factory = await AsyncDatabaseEngineFactoryProvider.get_factory(config)
    return await factory.create_engine()
